#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>

// if one thread exception occurred, the process to which it belongs will also terminate.
namespace test1
{
    void* handler(void* arg)
    {
        int count = 0;
        while(count < 5)
        {
            std::cout << "new thread is running..." << std::endl;
            ++count;
            sleep(1);
        }

        // make new thread break exception
        std::cout << "wild pointer will be call..." << std::endl;
        // int* p = nullptr;
        // *p = 100;

        return (int*)0;
    }

    void test()
    {
        pthread_t tid;
        pthread_create(&tid, nullptr, handler, nullptr);

        void* ret = nullptr;
        pthread_join(tid, &ret);
        std::cout << "main thread got value from new thread, val = " <<  (int*)ret << std::endl;
    }
}

// The stack between threads is independent of each other, 
// but it is necessary to avoid mutual influence between threads operating on the same block of address space
namespace test2
{
    void* PrintThreadName(void* arg)
    {
        const char* str = static_cast<char*>(arg);
        std::cout << str << std::endl;
        return nullptr;
    }

    void test()
    {
        pthread_t tid;
        std::vector<pthread_t> v;
        
        for(int i = 0; i < 5; i++)
        {
            char name[32];
            snprintf(name, sizeof(name), "Thread-%d", i + 1);
            pthread_create(&tid, nullptr, PrintThreadName, name);

            v.push_back(tid);
        }
        
        sleep(2);
        for(auto& e : v)
        {
            std::cout << "thread joining..." << std::endl;
            pthread_join(e, nullptr);
        }

        // ****** Print Result ******
        // Thread-3Thread-3
        // Thread-5
        // Thread-5
        // Thread-5
    }
    // create a variable in the heap area as a parameter to be passed to multiple threads, each pointing to a different address
    void newtest()
    {
        pthread_t tid;
        std::vector<pthread_t> v;
        
        for(int i = 0; i < 5; i++)
        {
            char* name = new char(32);
            snprintf(name, 32, "Thread-%d", i + 1);
            pthread_create(&tid, nullptr, PrintThreadName, name);

            v.push_back(tid);
        }
        
        sleep(2);
        for(auto& e : v)
        {
            void* p = nullptr;
            std::cout << "thread joining..." << std::endl;
            pthread_join(e, &p);
            delete[] p;     // attention to free the heap area memory
        }
    }
}

// send custom object pointer to pthread_create function
namespace test3
{
    class Date
    {
    public:
        Date(int year, int month, int day)
            :_year(year), _month(month), _day(day) {}
        Date& operator=(const Date& d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
            return *this;
        }
        ~Date() {}

        bool operator<(const Date& d)
        {
            if(_year < d._year)
            {
                return true;
            }
            else if(_year > d._year)
            {
                return false;
            }
            else
            {
                if(_month < d._month)
                {
                    return true;
                }
                else if(_month > d._month)
                {
                    return false;
                }
                else
                {
                    if(_day < d._day)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
            }
        }
    public:
        int _year;
        int _month;
        int _day;
    };

    void* GetLowerDate(void* arg)
    {
        std::pair<Date*, Date*>* p_pr = static_cast<std::pair<Date*, Date*>*>(arg);

        Date* tmp = nullptr;
        if(*(p_pr->first) < *(p_pr->second))
        {
            tmp = p_pr->first;
        }
        else{
            tmp = p_pr->second;
        }

        return (void*)tmp;
    }

    void test()
    {
        pthread_t tid;

        Date d1(2024, 5, 18);
        Date d2(2024, 5, 3);
        std::pair<Date*, Date*> pr(&d1, &d2);
        pthread_create(&tid, nullptr, GetLowerDate, &pr);

        void* ret = nullptr;
        pthread_join(tid, &ret);
        auto date = static_cast<Date*>(ret);

        std::cout << date->_year << "年 " 
            << date->_month << "月 "
            << date->_day << "日" << std::endl;
    }
}


int main()
{
    //test1::test();
    //test2::test();
    //test2::newtest();
    test3::test();

    return 0;
}


